home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Best of Shareware
/
Best of PC Windows Shareware 1.0 - Wayzata Technology (7111) (1993).iso
/
mac
/
ZIPPED
/
DOS
/
GRAPHICS
/
RAYSH386.ZIP
/
SRC
/
GRID.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-02-09
|
12KB
|
500 lines
/*
* grid.c
*
* Copyright (C) 1989, 1991, Craig E. Kolb
* All rights reserved.
*
* This software may be freely copied, modified, and redistributed
* provided that this copyright notice is preserved on all copies.
*
* You may not distribute this software, in whole or in part, as part of
* any commercial product without the express consent of the authors.
*
* There is no warranty or other guarantee of fitness of this software
* for any purpose. It is provided solely "as is".
*
* $Id: grid.c,v 4.0.1.1 91/10/04 15:55:37 cek Exp Locker: cek $
*
* $Log: grid.c,v $
* Revision 4.0.1.1 91/10/04 15:55:37 cek
* patch1: Removed straight floating point comparisons.
*
* Revision 4.0 91/07/17 14:38:02 kolb
* Initial version.
*
*/
#include "geom.h"
#include "grid.h"
static Methods *iGridMethods = NULL;
static char gridName[] = "grid";
static unsigned long raynumber = 1; /* Current "ray number". */
/* (should be "grid number") */
static void engrid(), GridFreeVoxels();
static int pos2grid(), CheckVoxel();
Grid *
GridCreate(x, y, z)
int x, y, z;
{
Grid *grid;
if (x < 1 || y < 1 || z < 1) {
RLerror(RL_WARN, "Invalid grid specification.\n");
return (Grid *)NULL;
}
grid = (Grid *)share_calloc(1, sizeof(Grid));
grid->xsize = x;
grid->ysize = y;
grid->zsize = z;
return grid;
}
char *
GridName()
{
return gridName;
}
/*
* Intersect ray with grid, returning distance from "pos" to
* nearest intersection with an object in the grid. Returns 0.
* if no intersection.
*/
int
GridIntersect(grid, ray, hitlist, mindist, maxdist)
Grid *grid;
Ray *ray;
HitList *hitlist;
Float mindist, *maxdist;
{
GeomList *list;
Geom *obj;
int hit;
Float offset, tMaxX, tMaxY, tMaxZ;
Float tDeltaX, tDeltaY, tDeltaZ, *raybounds[2][3];
int stepX, stepY, stepZ, outX, outY, outZ, x, y, z;
Vector curpos, nXp, nYp, nZp, np, pDeltaX, pDeltaY, pDeltaZ;
unsigned long counter;
hit = FALSE;
/*
* Check unbounded objects.
*/
for (obj = grid->unbounded ; obj; obj = obj->next) {
if (intersect(obj, ray, hitlist, mindist, maxdist))
hit = TRUE;
}
/*
* If outside of the bounding box, check to see if we
* hit it.
*/
VecAddScaled(ray->pos, mindist, ray->dir, &curpos);
if (OutOfBounds(&curpos, grid->bounds)) {
offset = *maxdist;
if (!BoundsIntersect(ray, grid->bounds, mindist, &offset))
/*
* Ray never hit grid space.
*/
return hit;
/*
* else
* The ray enters voxel space before it hits
* an unbounded object.
*/
VecAddScaled(ray->pos, offset, ray->dir, &curpos);
} else
offset = mindist;
counter = raynumber++;
/*
* tMaxX is the absolute distance from the ray origin we must move
* to get to the next voxel in the X
* direction. It is incrementally updated
* by DDA as we move from voxel-to-voxel.
* tDeltaX is the relative amount along the ray we move to
* get to the next voxel in the X direction. Thus,
* when we decide to move in the X direction,
* we increment tMaxX by tDeltaX.
*/
x = x2voxel(grid, curpos.x);
if (x == grid->xsize)
x--;
if (fabs(ray->dir.x) < EPSILON) {
tMaxX = FAR_AWAY;
raybounds[LOW][X] = &curpos.x;
raybounds[HIGH][X] = &np.x;
tDeltaX = 0.;
} else if (ray->dir.x < 0.) {
tMaxX = offset + (voxel2x(grid, x) - curpos.x) / ray->dir.x;
tDeltaX = grid->voxsize[X] / - ray->dir.x;
stepX = outX = -1;
raybounds[LOW][X] = &np.x;
raybounds[HIGH][X] = &curpos.x;
} else {
tMaxX = offset + (voxel2x(grid, x+1) - curpos.x) / ray->dir.x;
tDeltaX = grid->voxsize[X] / ray->dir.x;
stepX = 1;
outX = grid->xsize;
raybounds[LOW][X] = &curpos.x;
raybounds[HIGH][X] = &np.x;
}
y = y2voxel(grid, curpos.y);
if (y == grid->ysize)
y--;
if (fabs(ray->dir.y) < EPSILON) {
tMaxY = FAR_AWAY;
raybounds[LOW][Y] = &curpos.y;
raybounds[HIGH][Y] = &np.y;
tDeltaY = 0.;
} else if (ray->dir.y < 0.) {
tMaxY = offset + (voxel2y(grid, y) - curpos.y) / ray->dir.y;
tDeltaY = grid->voxsize[Y] / - ray->dir.y;
stepY = outY = -1;
raybounds[LOW][Y] = &np.y;
raybounds[HIGH][Y] = &curpos.y;
} else {
tMaxY = offset + (voxel2y(grid, y+1) - curpos.y) / ray->dir.y;
tDeltaY = grid->voxsize[Y] / ray->dir.y;
stepY = 1;
outY = grid->ysize;
raybounds[LOW][Y] = &curpos.y;
raybounds[HIGH][Y] = &np.y;
}
z = z2voxel(grid, curpos.z);
if (z == grid->zsize)
z--;
if (fabs(ray->dir.z) < EPSILON) {
tMaxZ = FAR_AWAY;
raybounds[LOW][Z] = &curpos.z;
raybounds[HIGH][Z] = &np.z;
tDeltaZ = 0.;
} else if (ray->dir.z < 0.) {
tMaxZ = offset + (voxel2z(grid, z) - curpos.z) / ray->dir.z;
tDeltaZ = grid->voxsize[Z] / - ray->dir.z;
stepZ = outZ = -1;
raybounds[LOW][Z] = &np.z;
raybounds[HIGH][Z] = &curpos.z;
} else {
tMaxZ = offset + (voxel2z(grid, z+1) - curpos.z) / ray->dir.z;
tDeltaZ = grid->voxsize[Z] / ray->dir.z;
stepZ = 1;
outZ = grid->zsize;
raybounds[LOW][Z] = &curpos.z;
raybounds[HIGH][Z] = &np.z;
}
VecScale(tDeltaX, ray->dir, &pDeltaX);
VecScale(tDeltaY, ray->dir, &pDeltaY);
VecScale(tDeltaZ, ray->dir, &pDeltaZ);
VecAddScaled(ray->pos, tMaxX, ray->dir, &nXp);
VecAddScaled(ray->pos, tMaxY, ray->dir, &nYp);
VecAddScaled(ray->pos, tMaxZ, ray->dir, &nZp);
while (TRUE) {
list = grid->cells[x][y][z];
if (tMaxX < tMaxY && tMaxX < tMaxZ) {
if (list) {
np = nXp;
if (CheckVoxel(list,ray,raybounds,
hitlist,counter,offset,maxdist))
hit = TRUE;
}
x += stepX;
if (*maxdist < tMaxX || x == outX)
break;
tMaxX += tDeltaX;
curpos = nXp;
nXp.x += pDeltaX.x;
nXp.y += pDeltaX.y;
nXp.z += pDeltaX.z;
} else if (tMaxZ < tMaxY) {
if (list) {
np = nZp;
if (CheckVoxel(list,ray, raybounds,
hitlist,counter,offset,maxdist))
hit = TRUE;
}
z += stepZ;
if (*maxdist < tMaxZ || z == outZ)
break;
tMaxZ += tDeltaZ;
curpos = nZp;
nZp.x += pDeltaZ.x;
nZp.y += pDeltaZ.y;
nZp.z += pDeltaZ.z;
} else {
if (list) {
np = nYp;
if (CheckVoxel(list,ray,raybounds,
hitlist,counter,offset,maxdist))
hit = TRUE;
}
y += stepY;
if (*maxdist < tMaxY || y == outY)
break;
tMaxY += tDeltaY;
curpos = nYp;
nYp.x += pDeltaY.x;
nYp.y += pDeltaY.y;
nYp.z += pDeltaY.z;
}
}
return hit;
}
/*
* Intersect ray with objects in grid cell. Note that there are a many ways
* to speed up this routine, all of which uglify the code to a large extent.
*/
static int
CheckVoxel(list,ray,raybounds,hitlist,counter,mindist,maxdist)
GeomList *list;
Ray *ray;
Float *raybounds[2][3];
HitList *hitlist;
unsigned long counter;
Float mindist, *maxdist;
{
Geom *obj;
int hit;
Float lx, hx, ly, hy, lz, hz;
lx = *raybounds[LOW][X];
hx = *raybounds[HIGH][X];
ly = *raybounds[LOW][Y];
hy = *raybounds[HIGH][Y];
lz = *raybounds[LOW][Z];
hz = *raybounds[HIGH][Z];
hit = FALSE;
do {
obj = list->obj;
/*
* If object's counter is greater than or equal to the
* number associated with the current grid,
* don't bother checking again. In addition, if the
* bounding box of the ray's extent in the voxel does
* not intersect the bounding box of the object, don't bother.
*/
#ifdef SHAREDMEM
if (*obj->counter < counter &&
#else
if (obj->counter < counter &&
#endif
obj->bounds[LOW][X] <= hx &&
obj->bounds[HIGH][X] >= lx &&
obj->bounds[LOW][Y] <= hy &&
obj->bounds[HIGH][Y] >= ly &&
obj->bounds[LOW][Z] <= hz &&
obj->bounds[HIGH][Z] >= lz) {
#ifdef SHAREDMEM
*obj->counter = counter;
#else
obj->counter = counter;
#endif
if (intersect(obj, ray, hitlist, mindist, maxdist))
hit = TRUE;
}
} while ((list = list->next) != (GeomList *)0);
return hit;
}
int
GridConvert(grid, objlist)
Grid *grid;
Geom *objlist;
{
int num;
/*
* Keep linked list of all bounded objects in grid; it may come
* in handy.
*/
grid->objects = objlist;
for (num = 0; objlist; objlist = objlist->next)
num += objlist->prims;
return num;
}
void
GridBounds(grid, bounds)
Grid *grid;
Float bounds[2][3];
{
Geom *obj, *ltmp;
int x, y, i;
BoundsInit(bounds);
/*
* For each object on the list,
* compute its bounds...
*/
/*
* Find bounding box of bounded objects and get list of
* unbounded objects.
*/
grid->unbounded = GeomComputeAggregateBounds(&grid->objects,
grid->unbounded, grid->bounds);
BoundsCopy(grid->bounds, bounds);
grid->voxsize[X] = (grid->bounds[HIGH][X]-grid->bounds[LOW][X])/
grid->xsize;
grid->voxsize[Y] = (grid->bounds[HIGH][Y]-grid->bounds[LOW][Y])/
grid->ysize;
grid->voxsize[Z] = (grid->bounds[HIGH][Z]-grid->bounds[LOW][Z])/
grid->zsize;
if (grid->cells == (GeomList ****)NULL) {
/*
* Allocate voxels.
*/
grid->cells = (GeomList ****)share_malloc(grid->xsize *
sizeof(GeomList ***));
for (x = 0; x < grid->xsize; x++) {
grid->cells[x] = (GeomList ***)share_malloc(grid->ysize *
sizeof(GeomList **));
for (y = 0; y < grid->ysize; y++)
grid->cells[x][y] = (GeomList **)share_calloc(
(unsigned)grid->zsize,sizeof(GeomList *));
}
} else {
/*
* New frame...
* Free up the objlists in each voxel.
*/
GridFreeVoxels(grid);
}
/*
* objlist now holds a linked list of bounded objects.
*/
for (ltmp = grid->objects; ltmp != (Geom *)0; ltmp = ltmp->next)
engrid(ltmp, grid);
}
static void
GridFreeVoxels(grid)
Grid *grid;
{
int x, y, z;
GeomList *cell, *next;
for (x = 0; x < grid->xsize; x++) {
for (y = 0; y < grid->ysize; y++) {
for (z = 0; z < grid->zsize; z++) {
for (cell = grid->cells[x][y][z]; cell; cell = next) {
next = cell->next;
free((voidstar)cell);
}
grid->cells[x][y][z] = (GeomList *)NULL;
}
}
}
}
Methods *
GridMethods()
{
if (iGridMethods == (Methods *)NULL) {
iGridMethods = MethodsCreate();
iGridMethods->methods = GridMethods;
iGridMethods->create = (GeomCreateFunc *)GridCreate;
iGridMethods->intersect = GridIntersect;
iGridMethods->name = GridName;
iGridMethods->convert = GridConvert;
iGridMethods->bounds = GridBounds;
iGridMethods->checkbounds = FALSE;
iGridMethods->closed = TRUE;
}
return iGridMethods;
}
/*
* Place an object in a grid.
*/
static void
engrid(obj, grid)
Geom *obj;
Grid *grid;
{
int x, y, z, low[3], high[3];
GeomList *ltmp;
/*
* This routine should *never* be passed an unbounded object, but...
*/
if (!pos2grid(grid, obj->bounds[LOW], low) ||
!pos2grid(grid, obj->bounds[HIGH], high) ||
obj->bounds[LOW][X] > obj->bounds[HIGH][X]) {
/*
* Geom is partially on wholly outside of
* grid -- this should never happen, but just
* in case...
*/
RLerror(RL_ABORT, "Engrid got an unbounded object?!\n");
return;
}
/*
* For each voxel that intersects the object's bounding
* box, add pointer to this object to voxel's linked list.
*/
for (x = low[X]; x <= high[X]; x++) {
for (y = low[Y]; y <= high[Y]; y++) {
for (z = low[Z]; z <= high[Z]; z++) {
ltmp = (GeomList *)share_malloc(sizeof(GeomList));
ltmp->obj = obj;
ltmp->next = grid->cells[x][y][z];
grid->cells[x][y][z] = ltmp;
}
}
}
}
/*
* Convert 3D point to index into grid's voxels.
*/
static int
pos2grid(grid, pos, index)
Grid *grid;
Float pos[3];
int index[3];
{
index[X] = (int)(x2voxel(grid, pos[0]));
index[Y] = (int)(y2voxel(grid, pos[1]));
index[Z] = (int)(z2voxel(grid, pos[2]));
if (index[X] == grid->xsize)
index[X]--;
if (index[Y] == grid->ysize)
index[Y]--;
if (index[Z] == grid->zsize)
index[Z]--;
if (index[X] < 0 || index[X] >= grid->xsize ||
index[Y] < 0 || index[Y] >= grid->ysize ||
index[Z] < 0 || index[Z] >= grid->zsize)
return FALSE;
return TRUE;
}
void
GridMethodRegister(meth)
UserMethodType meth;
{
if (iGridMethods)
iGridMethods->user = meth;
}